package com.zhilei.controller;

import com.google.gson.Gson;
import com.zhilei.base.BaseInfoProperties;
import com.zhilei.grace.result.GraceJSONResult;
import com.zhilei.grace.result.ResponseStatusEnum;
import com.zhilei.mq.RabbitMQSMSConfig;
import com.zhilei.pojo.Users;
import com.zhilei.pojo.bo.RegistLoginBO;
import com.zhilei.pojo.mq.SMSContentQO;
import com.zhilei.pojo.vo.UsersVO;
import com.zhilei.service.UsersService;
import com.zhilei.untils.IPUtil;
import com.zhilei.untils.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.UUID;

/**
 * 接收前端请求的数据的请求体，使用 @RequestBody 这会将请求的正文，就是请求的JSON转换为Java对象
 * 返回给前端数据，也就是将数据写入到JSON中发送给前端，使用 @ResponseBody
 *
 * @author zhilei
 * @data 2024-09-04
 */
@RestController
@RequestMapping("passport")
@Slf4j
public class PassportController extends BaseInfoProperties {

    @Autowired
    private UsersService usersService;

    @Autowired
    private JWTUtils jwtUtils;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 根据手机号获取验证码
     *
     * @param mobile  手机号
     * @param request Request
     * @return
     */
    @PostMapping("getSMSCode")
    @Retryable(value = {Exception.class},
            maxAttempts = 5,
            backoff = @Backoff(delay = 2000, multiplier = 2))
    public GraceJSONResult getSMSCode(@RequestParam("mobile") String mobile,
                                      HttpServletRequest request) {
        // TODO 1. 校验用户的手机号是否存在，如果没有手机号，就直接抛出异常就可以
        // TODO 提示用户没有手机号，不允许进行注册
        if (StringUtils.isBlank(mobile)) {
            return GraceJSONResult.errorMsg("手机号不能为空");
        }

        // TODO 2. 校验60s内同一个ip地址只能发送一次手机验证码
        // 2.1 获取用户ip地址，因为每一台电脑在网络中有一个唯一的IP地址
        // 在spring中，请求会先经过过滤器，在经过拦截器；
        // 过滤器是基于Servlet规范的组件，它可以对请求进行预处理和后处理，可以在请求到达Servlet之前对请求进行过滤和处理。
        // 拦截器是spring自带的组件，是面向AOP切面编程的一种实现，在请求到达控制器之前进行处理
        String ip = IPUtil.getRequestIp(request);
        // 2.2 将ip设置60s的时效,在这60s内，只能设置一个验证码；把ip存放到redis中，设置60s的时间限制
        // 只要请求打过来之后，去判断一下redis中是不是有这个key就可以了
        redis.setnx60s(MOBILE_SMSCODE + ":" + ip, mobile);

        // TODO 生成6位数字的随机验证码，放到redis中，时间是5分钟
        String code = String.valueOf((int) (Math.random() * 900000 + 100000));
        log.info("验证码是： {}", code);

        // rabbitMQ异步解耦发送消息
        SMSContentQO smsContentQO = new SMSContentQO();
        smsContentQO.setMobile(mobile);
        smsContentQO.setContent(code);

        // 发送消息到rabbitmq，交换机是sms.direct，routingKey是sms.direct
        rabbitTemplate.convertAndSend(RabbitMQSMSConfig.SMS_EXCHANGE,
                RabbitMQSMSConfig.ZHILEI_SMS_SEND,
                new Gson().toJson(smsContentQO),
                new CorrelationData(UUID.randomUUID().toString()));

        // TODO 将验证码存储到redis中-5分钟的有效期
        redis.set(MOBILE_SMSCODE + ":" + mobile, code, 15 * 60);
        // 返回的格式
        return GraceJSONResult.ok();
    }

    /**
     * 传入手机号，验证码进行用户注册登录
     *
     * @param registLoginBO 手机号，验证码
     * @return
     */
    @PostMapping("login")
    public GraceJSONResult login(@Valid @RequestBody RegistLoginBO registLoginBO,
                                 HttpServletRequest request) {

        log.error("Data.toString: {}", registLoginBO.toString());
        log.error("Data.hashCode: {}", registLoginBO.hashCode());

        // TODO 1. 获取传入的手机号跟验证码
        String mobile = registLoginBO.getMobile();
        String smsCode = registLoginBO.getSmsCode();

        // TODO 2. 校验redis的验证码跟传进来的验证码是不是一样的
        String smsCodeByRedis = redis.get(MOBILE_SMSCODE + ":" + mobile);
        if (StringUtils.isBlank(smsCodeByRedis) || !smsCodeByRedis.equalsIgnoreCase(smsCode)) {
            return GraceJSONResult.errorMsg("验证码不正确");
        }

        // TODO 3. 通过手机号查询用户是否存在,已经存在了，就直接登录就行了
        Users byMobile = usersService.getByMobile(mobile);
        if (byMobile == null) {
            // 不存在就注册
            byMobile = usersService.register(registLoginBO);
        }

        // TODO 使用UUID，生成redisToken
        // String uuid = UUID.randomUUID().toString().replace("-", "");
        // log.warn("UUID: {}", uuid);

        // TODO 3. 存入redis中，并且设置过期时间
        // String userId = byMobile.getId();
        // redis.set(REDIS_USER_TOKEN + ":" + userId, uuid, 7 * 24 * 60 * 60);

        // TODO 4. 使用没有状态的jwt
        String jwtToken = jwtUtils.createJWT(new Gson().toJson(byMobile), TOKEN_USER_PREFIX);

        UsersVO usersVO = new UsersVO();
        BeanUtils.copyProperties(byMobile, usersVO);
        usersVO.setUserToken(jwtToken);

        // TODO 3. 删除redis中存放的验证码
        redis.del(MOBILE_SMSCODE + ":" + mobile);
        return GraceJSONResult.ok(usersVO);
    }


    /**
     * 退出登录
     *
     * @param userId
     * @return
     */
    @PostMapping("logout")
    public GraceJSONResult logout(String userId) {
        // TODO 1. 删除redis中存放的token
        log.error("userId: {}", userId);
        // redis.del(REDIS_USER_TOKEN + ":" + userId);
        return GraceJSONResult.ok();
    }

    @Recover
    public GraceJSONResult recover(Exception e) {
        // 处理重试失败的逻辑
        log.error("重试发送短信验证码失败：{}", e.getMessage(), e);
        return GraceJSONResult.errorCustom(ResponseStatusEnum.USER_STATUS_ERROR);
    }
}
